package com.zx._03_并发.chapter_03_volatile;

import org.openjdk.jol.info.ClassLayout;

public class 内存伪共享 implements Runnable {

    private final static int NUM_THREADS = 4; // change
    private final static long ITERATIONS = 500L * 1000L * 1000L;
    private static VolatileLong[] longs = new VolatileLong[NUM_THREADS];

    static {
        for (int i = 0; i < longs.length; i++) {
            longs[i] = new VolatileLong();
        }
    }

    private final int arrayIndex;

    public 内存伪共享(final int arrayIndex) {
        this.arrayIndex = arrayIndex;
    }

    public static void main(final String[] args) throws Exception {
        final long start = System.nanoTime();
        runTest();
        System.out.println("duration = " + (System.nanoTime() - start));

        VolatileLong obj = new VolatileLong();
        String printable = ClassLayout.parseInstance(obj).toPrintable();
        System.out.println(printable);
		/*
		 对象内存分析
		 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
		      0     4        (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)
		      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
		      8     4        (object header)                           4e 40 17 00 (01001110 01000000 00010111 00000000) (1523790)
		     12     4        (alignment/padding gap)                  
		     16     8   long VolatileLong.value                        0
		     24     8   long VolatileLong.p1                           0
		     32     8   long VolatileLong.p2                           0
		     40     8   long VolatileLong.p3                           0
		     48     8   long VolatileLong.p4                           0
		     56     8   long VolatileLong.p5                           0
		     64     8   long VolatileLong.p6                           0
		Instance size: 64 bytes
		Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
		 */
    }

    private static void runTest() throws InterruptedException {
        Thread[] threads = new Thread[NUM_THREADS];

        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(new 内存伪共享(i));
        }
        for (Thread t : threads) {
            t.start();
        }
        for (Thread t : threads) {
            t.join();
        }
    }

    @Override
    public void run() {
        long i = ITERATIONS + 1;
        while (0 != --i) {
            longs[arrayIndex].value = i;
        }
    }

    public final static class VolatileLong {

        public volatile long value = 0L;
        // 64位计算机， java object 对象，markWord=8b  classPointer=4b, instanceData=0b, padding=4b。一个引用指针大小是4b
        // 所以一个VolatileLong对象，markWord=8b  classPointer=4b, 每个long占用8b，所以instanceData=8*7=56, padding进行补齐=4b，一共是72b
        public long p1, p2, p3, p4, p5, p6;
    }
    // 使用对象对齐duration =  9862284500
    // 去掉对象对齐duration = 26470602200
	/*
		可以看到，两个程序逻辑完全一致，只是注释掉了几个没有使用到的变量，却导致性能相差很大。 
		我们知道一条缓存行有64字节, 而Java程序的对象头固定占8字节(32位系统)或12字节(64位系统默认开启压缩, 不开压缩为16字节). 
		我们只需要填6个无用的长整型补上6*8=48字节, 让不同的VolatileLong对象处于不同的缓存行, 
		就可以避免伪共享了(64位系统超过缓存行的64字节也无所谓,只要保证不同线程不要操作同一缓存行就可以)。
		这个办法叫做补齐(Padding)。
	*/
}
